﻿using FPSO.Models.Constant;
using FPSO.Models.Domain;
using FPSO.Models.ViewModel.Controls;
using NSubstitute.Core;
using PrismMVVMLibrary.DesignTime;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static FPSO.Models.Constant.TankConstant;

namespace FPSO.Services.Impl
{
    public class ImitateExecuteTaskService<T> 
    {
        private T viewModel;
        private List<TankViewModel> wbtTankViewList;
        private List<TankOilViewModel> oilTankViewList;
        private List<ValveViewModel> valveViewList;
        private List<PumpViewModel> pumpViewList;
        private ITankTaskService taskService;
        private ImitateTankLevelService tankLevelService;
        private DateTime prevTime;

        public ImitateExecuteTaskService(T viewModel, ITankTaskService taskService)
        {
            this.viewModel = viewModel;
            this.taskService = taskService;
            this.wbtTankViewList = new List<TankViewModel>();
            this.oilTankViewList = new List<TankOilViewModel>();
            this.valveViewList = new List<ValveViewModel>();
            this.pumpViewList = new List<PumpViewModel>();


            this.tankLevelService = new ImitateTankLevelService();
            this.prevTime = DateTime.MinValue;

            PropertyInfo[] propertys = typeof(T).GetProperties();
            foreach(PropertyInfo p in propertys)
            {
                object propVal = p.GetValue(viewModel);
                if (propVal is TankViewModel t)
                {
                    wbtTankViewList.Add(t);
                }
                else if (propVal is TankOilViewModel o)
                {
                    oilTankViewList.Add(o);
                }
                else if (propVal is ValveViewModel v)
                {
                    valveViewList.Add(v);
                }
                else if (propVal is PumpViewModel pump)
                {
                    pumpViewList.Add(pump);
                }
            }
        }

        public void ReflushData(DateTime lastRefreshTime, DateTime currentTime)
        {
			//DEBUG openValveSet和activeTankSet有什么用
			HashSet<string> openValveSet = new HashSet<string>();
			HashSet<string> closeValveSet = new HashSet<string>();
			HashSet<string> activeTankSet = new HashSet<string>();
            List<TankTask> taskList = taskService.GetAllWaitAndExecingAndCheckingTaskList();
            
            foreach(TankTask task in taskList)
            {
				//这里汇总计算判断有些问题，当前时间如早于任务开始时间，则该任务无舱室变化；如当前时间晚于任务结束时间，则该任务已完成，需将舱室变化全部计入
				//if (currentTime < task.PlanStartTime || currentTime > task.PlanEndTime)
				//{
				//    continue;
				//}
				//prevTime = task.PlanStartTime;
				prevTime = lastRefreshTime;

				DateTime localCalcTime = currentTime;
				if (currentTime < task.PlanStartTime)
					continue;
				else if (currentTime > task.PlanEndTime)
					localCalcTime = task.PlanEndTime;
				
				if (task.TaskType == TaskTypeConstant.LOAD_ID)
                {
					ImitateLoadTask(task, localCalcTime, openValveSet, closeValveSet, activeTankSet);
                }
                else if (task.TaskType == TaskTypeConstant.UNLOAD_ID)
                {
                    ImitateOutTask(task, localCalcTime, openValveSet, closeValveSet, activeTankSet);
                }
                else if (task.TaskType == TaskTypeConstant.SWAP_ID)
                {
                    ImitateSwapTask(task, localCalcTime, openValveSet, closeValveSet, activeTankSet);
                }
            }

            foreach(ValveViewModel valve in valveViewList)
            {
				//先把该关的关闭，再开启所有应该开启的
				valve.StatusClosed = closeValveSet.Contains(valve.ValveId);
				valve.StatusOpened = openValveSet.Contains(valve.ValveId);
			}

            //foreach (PumpViewModel pump in pumpViewList)
            //{
            //    pump.Running = openValveSet.Contains(pump.PumpId);
            //}
        }

        private void ImitateLoadTask(TankTask task, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet)
        {
			//这里需要将所有舱（包含货油和压载舱）的状态都作为参数传入safeload，而不仅是任务目标舱
            string[] tankdis = task.TankId.Split(',');
			if (tankdis.Length == 0)
				return;

			Dictionary<string, double> taskTankVolume = new Dictionary<string, double>();
			foreach (TankOilViewModel tankModel in oilTankViewList)
			{
				if(tankModel != null)
				{
					string tankid = tankModel.TankId;
					bool exist = false;
					for(int i=0;i< tankdis.Length;i++)
					{
						string tankdisi = tankdis[i];
						if(tankdisi == tankid)
						{
							exist = true;
							break;
						}
					}
					string tankname = TankConstant.GetTankName(tankModel.TankId);
					double currentvolume = tankModel.Volume;
					if (exist)
					{
						//double leaveHours = (task.PlanEndTime - currentTime).TotalHours;
						double leaveHours = (currentTime - task.PlanStartTime).TotalHours;
						currentvolume = tankModel.TotalVolume * task.InitVolm * 0.01 + task.PipeSpeed * leaveHours;
						double targetvolume = tankModel.TotalVolume * task.TargetVolm * 0.01;
						if (currentvolume > targetvolume)
							currentvolume = targetvolume;
						tankModel.Volume = currentvolume;

						//DEBUG 监测装卸执行进度，当返回装卸速率大于0时，对应的阀门开启，否则全部关闭
						//WARNING 这里设备状态颜色转换可能有问题
						if (tankModel.Rate > 1.0)
						{
							List<ConstantTaskDevice> taskDevices = TankConstant.GetTaskRelationValve("", tankModel.TankId, TaskTypeConstant.LOAD_ID);
							if (taskDevices != null)
							{
								foreach (ConstantTaskDevice taskDevice in taskDevices)
								{
									ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskDevice.DeviceId);
									if (taskDevice.Operates[0] == DEVICE_OPERATE.TRUE)
										openValveSet.Add(taskDevice.DeviceId);
									else if (taskDevice.Operates[0] == DEVICE_OPERATE.FALSE)
										closeValveSet.Add(taskDevice.DeviceId);
								}
							}
						}
						else
						{
							List<ConstantTaskDevice> taskDevices = TankConstant.GetTaskRelationValve("", tankModel.TankId, TaskTypeConstant.LOAD_ID);
							if (taskDevices != null)
							{
								foreach (ConstantTaskDevice taskDevice in taskDevices)
								{
									ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskDevice.DeviceId);
									if (taskDevice.Operates[0] == DEVICE_OPERATE.TRUE)
										closeValveSet.Add(taskDevice.DeviceId);
								}
							}
						}
					}
					taskTankVolume.Add(tankid, currentvolume);
				}
			}

			ImitateFullWBTTask(task, currentTime, openValveSet, closeValveSet, tankSet, taskTankVolume);

			SafeLoadAlgorithmService algorithmService = SafeLoadAlgorithmService.GetInstance();
			ManualCalcResult calcResult = algorithmService.CallManualCalc(taskTankVolume, 3);

			/*foreach (string tankid in tankdis)
			{
				TankOilViewModel tankModel = oilTankViewList.Where(t => t.TankId == task.TankId).FirstOrDefault();
				if (tankModel != null)
				{
					string tankname = TankConstant.GetTankName(tankModel.TankId);
					double leaveHours = (task.PlanEndTime - currentTime).TotalHours;
					tankModel.Volume = tankModel.TotalVolume * task.TargetVolm * 0.01 - task.PipeSpeed * leaveHours;

					SafeLoadAlgorithmService safeloadAlgorithm = SafeLoadAlgorithmService.GetInstance();
					//Tips：C++端接口新增SingleTank计算方法，每次仅支持单一舱室的计算，详见《C++端接口调用说明》
					string inputstr = "{Manual}#{volume}#{" + string.Join("#", tankname, tankModel.Volume.ToString() + "#}");

					ManualCalcResult result = safeloadAlgorithm.CallPipeSingleCalc(inputstr, tankModel.TankId);
					if (result != null)
					{
						//Tips：其余控件赋值来源于接口计算结果
						TankCalcState tankState = result.GetTankCalcState(tankModel.TankId);
						tankModel.Rate = tankState.Rate;
						tankModel.Level = tankState.Inn;
					}
					//tankModel.Rate = task.PipeSpeed;
					tankSet.Add(tankModel.TankId);
				}
			}*/

            //ImitateWBTTask(task, currentTime, valveSet, tankSet);
        }
		private void ImitateOutTask(TankTask task, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet)
		{
			string[] tankdis = task.TankId.Split(',');
			if (tankdis.Length == 0)
				return;

			Dictionary<string, double> taskTankVolume = new Dictionary<string, double>();
			foreach (TankOilViewModel tankModel in oilTankViewList)
			{
				if (tankModel != null)
				{
					string tankid = tankModel.TankId;
					bool exist = false;
					for (int i = 0; i < tankdis.Length; i++)
					{
						string tankdisi = tankdis[i];
						if (tankdisi == tankid)
						{
							exist = true;
							break;
						}
					}
					string tankname = TankConstant.GetTankName(tankModel.TankId);
					double currentvolume = tankModel.Volume;
					if (exist)
					{
						double leaveHours = (currentTime - task.PlanStartTime).TotalHours;
						currentvolume = tankModel.TotalVolume * task.InitVolm * 0.01 - task.PipeSpeed * leaveHours;
						double targetvolume = tankModel.TotalVolume * task.TargetVolm * 0.01;
						if (currentvolume < targetvolume)
							currentvolume = targetvolume;
						//double leaveHours = (task.PlanEndTime - currentTime).TotalHours;
						//currentvolume = tankModel.TotalVolume * task.TargetVolm * 0.01 + task.PipeSpeed * leaveHours;
						tankModel.Volume = currentvolume;
					}
					taskTankVolume.Add(tankid, currentvolume);
				}
			}
			ImitateFullWBTTask(task, currentTime, openValveSet, closeValveSet, tankSet, taskTankVolume);
			SafeLoadAlgorithmService algorithmService = SafeLoadAlgorithmService.GetInstance();
			ManualCalcResult calcResult = algorithmService.CallManualCalc(taskTankVolume, 3);

			List<ConstantTaskDevice> taskDevices = TankConstant.GetTaskRelationValve(task.TankId, "", TaskTypeConstant.UNLOAD_ID);
			if (taskDevices != null)
			{
				foreach (ConstantTaskDevice taskDevice in taskDevices)
				{
					ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskDevice.DeviceId);
					if (taskDevice.Operates[0] == DEVICE_OPERATE.TRUE)
						openValveSet.Add(taskDevice.DeviceId);
					else if (taskDevice.Operates[0] == DEVICE_OPERATE.FALSE)
						closeValveSet.Add(taskDevice.DeviceId);
				}
			}
		}
		private void ImitateSwapTask(TankTask task, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet)
		{
			string[] tankdis = task.TankId.Split(',');
			string[] tanktargetdis = task.SwapTargetTank1.Split(',');

			if (tankdis.Length == 0 || tanktargetdis.Length == 0)
				return;

			Dictionary<string, double> taskTankVolume = new Dictionary<string, double>();
			foreach (TankOilViewModel tankModel in oilTankViewList)
			{
				if (tankModel != null)
				{
					string tankname = TankConstant.GetTankName(tankModel.TankId);
					double currentvolume = tankModel.Volume;

					string tankid = tankModel.TankId;
					bool exist = false;
					for (int i = 0; i < tankdis.Length; i++)
					{
						string tankdisi = tankdis[i];
						if (tankdisi == tankid)
						{
							exist = true;
							break;
						}
					}
					//这里exist说明是导出舱，为unload过程
					if (exist)
					{
						double leaveHours = (currentTime - task.PlanStartTime).TotalHours;
						currentvolume = tankModel.TotalVolume * task.InitVolm * 0.01 - task.PipeSpeed * leaveHours;
						double targetvolume = tankModel.TotalVolume * task.TargetVolm * 0.01;
						if (currentvolume < targetvolume)
							currentvolume = targetvolume;
						tankModel.Volume = currentvolume;
					}
					else
					{
						for (int i = 0; i < tanktargetdis.Length; i++)
						{
							string tankdisi = tanktargetdis[i];
							if (tankdisi == tankid)
							{
								exist = true;
								break;
							}
						}
						//这里exist说明是导入舱，为load过程
						if (exist)
						{
							double leaveHours = (currentTime - task.PlanStartTime).TotalHours;
							currentvolume = tankModel.TotalVolume * task.InitVolm * 0.01 + task.PipeSpeed * leaveHours;
							double targetvolume = tankModel.TotalVolume * task.TargetVolm * 0.01;
							if (currentvolume > targetvolume)
								currentvolume = targetvolume;
							tankModel.Volume = currentvolume;
						}
					}
					taskTankVolume.Add(tankid, currentvolume);
				}
			}
			ImitateFullWBTTask(task, currentTime, openValveSet, closeValveSet, tankSet, taskTankVolume);

			SafeLoadAlgorithmService algorithmService = SafeLoadAlgorithmService.GetInstance();
			ManualCalcResult calcResult = algorithmService.CallManualCalc(taskTankVolume, 3);

			List<ConstantTaskDevice> taskDevices = TankConstant.GetTaskRelationValve(task.TankId, "", TaskTypeConstant.UNLOAD_ID);
			if (taskDevices != null)
			{
				foreach (ConstantTaskDevice taskDevice in taskDevices)
				{
					ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskDevice.DeviceId);
					if (taskDevice.Operates[0] == DEVICE_OPERATE.TRUE)
						openValveSet.Add(taskDevice.DeviceId);
					else if (taskDevice.Operates[0] == DEVICE_OPERATE.FALSE)
						closeValveSet.Add(taskDevice.DeviceId);
				}
			}
		}

		private void ImitateFullWBTTask(TankTask task, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet, Dictionary<string, double> taskTankVolume)
		{
			List<WBTTask> wbtTaskList = task.WBTTaskList;
			foreach (TankViewModel tankModel in wbtTankViewList)
			{
				if (tankModel != null)
				{
					string tankid = tankModel.TankId;
					if (task.WBTTaskList != null && task.WBTTaskList.Count > 0)
					{
						foreach (WBTTask wbtTask in task.WBTTaskList)
						{
							if(tankid == wbtTask.WBTTankId)
							{
								if (wbtTask.IsLoad)
								{
									ImitateWBTLoad(task, wbtTask, tankModel, currentTime, openValveSet, closeValveSet, tankSet);
								}
								else
								{
									ImitateWBTUnload(task, wbtTask, tankModel, currentTime, openValveSet, closeValveSet, tankSet);
								}
								break;
							}
						}
					}
					taskTankVolume.Add(tankid, tankModel.Volume);
				}
			}
		}
		private void ImitateWBTLoad(TankTask task, WBTTask wbtTask, TankViewModel wbtModel, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet )
        {
            if (wbtModel != null)
            {
                //压仓装载
                //if (prevTime != DateTime.MinValue)
                {
                    if (wbtModel.Volume >= wbtModel.TotalVolume * wbtTask.TargetVolume / 100)
                        return;
                    //计算时间差河给定的速度,

                    double outVolume = (currentTime - prevTime).TotalHours * wbtTask.PipeSpeed;
                    if (wbtModel.Volume+outVolume > wbtTask.TargetVolume*wbtModel.TotalVolume/100)
                    {
                        wbtModel.Volume = wbtTask.TargetVolume * wbtModel.TotalVolume / 100;
                        wbtModel.Rate = 0;
                    }
                    else
                    {
                        wbtModel.Volume += outVolume;
                        wbtModel.Rate = wbtTask.PipeSpeed;
                    }
					//DEBUG 监测装卸执行进度，当返回装卸速率大于0时，对应的阀门开启，否则全部关闭
					if (wbtModel.Rate > 1.0)
					{
						List<ConstantTaskDevice> taskWBTDevices = TankConstant.GetTaskRelationValve("", wbtTask.WBTTankId, TaskTypeConstant.BALLAST_ID);
						if (taskWBTDevices != null)
						{
							foreach (ConstantTaskDevice taskWBTDevice in taskWBTDevices)
							{
								ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskWBTDevice.DeviceId);
								if (taskWBTDevice.Operates[0] == DEVICE_OPERATE.TRUE)
									openValveSet.Add(taskWBTDevice.DeviceId);
								else if (taskWBTDevice.Operates[0] == DEVICE_OPERATE.FALSE)
									closeValveSet.Add(taskWBTDevice.DeviceId);
							}
						}
					}
					else
					{
						List<ConstantTaskDevice> taskWBTDevices = TankConstant.GetTaskRelationValve("", wbtTask.WBTTankId, TaskTypeConstant.BALLAST_ID);
						if (taskWBTDevices != null)
						{
							foreach (ConstantTaskDevice taskWBTDevice in taskWBTDevices)
							{
								ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskWBTDevice.DeviceId);
								if (taskWBTDevice.Operates[0] == DEVICE_OPERATE.TRUE)
									closeValveSet.Add(taskWBTDevice.DeviceId);
							}
						}
					}
				}
				tankSet.Add(wbtTask.WBTTankId);
            }
        }
        private void ImitateWBTUnload(TankTask task, WBTTask wbtTask, TankViewModel wbtModel, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet)
        {
            if (wbtModel != null && wbtModel.Volume > 0)
            {
                //压载舱，出舱
                //if (prevTime != DateTime.MinValue)
                {
                    if (wbtModel.Volume <= wbtModel.TotalVolume * wbtTask.TargetVolume / 100)
                        return;
                    //计算时间差河给定的速度,
                    double outVolume = (currentTime - prevTime).TotalHours * wbtTask.PipeSpeed;
                    if (wbtModel.Volume - outVolume < wbtModel.TotalVolume * wbtTask.TargetVolume / 100)
                    {
                        wbtModel.Volume = wbtModel.TotalVolume * wbtTask.TargetVolume / 100;
                        wbtModel.Rate = 0;
                    }
                    else
                    {
                        wbtModel.Volume -= outVolume;
                        wbtModel.Rate = wbtTask.PipeSpeed;

                        List<ConstantTaskDevice> taskWBTDevices = TankConstant.GetTaskRelationValve(wbtTask.WBTTankId, "", TaskTypeConstant.UNBALLAST_ID);
                        if (taskWBTDevices != null)
                        {
                            foreach (ConstantTaskDevice taskWBTDevice in taskWBTDevices)
                            {
                                ConstantDeviceInfo deviceInfo = TankConstant.getDeviceInfo(taskWBTDevice.DeviceId);
                                if (taskWBTDevice.Operates[0] == DEVICE_OPERATE.TRUE)
									openValveSet.Add(taskWBTDevice.DeviceId);
								else if (taskWBTDevice.Operates[0] == DEVICE_OPERATE.FALSE)
									closeValveSet.Add(taskWBTDevice.DeviceId);
							}
						}
                    }
                }
                tankSet.Add(wbtTask.WBTTankId);
            }
        }

		//DEBUG ImitateWBTTask由ImitateFullWBTTask取代
		private void ImitateWBTTask(TankTask task, DateTime currentTime, HashSet<string> openValveSet, HashSet<string> closeValveSet, HashSet<string> tankSet)
		{
			if (task.WBTTaskList != null && task.WBTTaskList.Count > 0)
			{
				foreach (WBTTask wbtTask in task.WBTTaskList)
				{
					TankViewModel wbtModel = wbtTankViewList.Where(t => t.TankId == wbtTask.WBTTankId).FirstOrDefault();

					if (wbtTask.IsLoad)
					{
						ImitateWBTLoad(task, wbtTask, wbtModel, currentTime, openValveSet, closeValveSet, tankSet);
					}
					else
					{
						ImitateWBTUnload(task, wbtTask, wbtModel, currentTime, openValveSet, closeValveSet, tankSet);
					}
				}
			}
		}
    }
}
